package com.africanmoviesmobile.webservice;

import java.io.File;
import java.io.IOException;
import java.util.Hashtable;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.pixmob.httpclient.HttpClient;
import org.pixmob.httpclient.HttpClientException;
import org.pixmob.httpclient.HttpResponse;

import android.content.Context;

import com.africanmoviesmobile.webservice.inf.Celeb;
import com.africanmoviesmobile.webservice.inf.Country;
import com.africanmoviesmobile.webservice.inf.Movie;
import com.africanmoviesmobile.webservice.inf.User;
import com.africanmoviesmobile.webservice.inf.WebserviceObject;

public class AmmWebService implements WebserviceObject{

	protected HttpClient _http;
	private final Context _context;
	
	// URLs
	protected static final String SERVICE_URL = "https://africanmoviesmobile.com/webservice";
	
	public static final String DEVICE = "android";
	public static final String DEVICE_TYPE = "mobile";
	
	private int _resultPageCount;

	public static AmmWebService getInstance(Context context){
		return new AmmWebService(context);
	}
	
	public AmmWebService(Context context) {
		_context = context;
		_http = new HttpClient(context);
	}

	Context getContext() {
        return _context;
    }
	
	protected StringBuilder checkResponse(StringBuilder response) throws WebServiceException{
		
		StringBuilder result = null;
		
		try {
			
			if ((response != null) && (response.length() > 0)) {
				
				if ((response.charAt(0) == '{') && (response.charAt(response.length() - 1) == '}')) {
					
					JSONObject jo = new JSONObject(new JSONTokener(response.toString()));
					
					if ((jo != null) && jo.has("errors")) {
						
						StringBuilder errorMesage = new StringBuilder();
						
						JSONArray ja = jo.optJSONArray("errors");
						
						if ((ja != null) && (ja.length() > 0)) {
														
							for (int i = 0; i < ja.length(); i++) {
								JSONObject error = ja.optJSONObject(i);
								errorMesage.append(error.optString("message") + "\r\n");
							}
						}
						
						throw new WebServiceException(errorMesage.toString());
						
					}
				}
			}
			
			result = response;
			
		} catch (JSONException e) {
			e.printStackTrace();
		}
		
		return result;
	}
	
	protected JSONObject read(String path) throws WebServiceException, HttpClientException, IOException {
		return read(path, null);
	}

	protected JSONObject read(String path, Hashtable<String, String> params) throws HttpClientException, IOException, WebServiceException {
		
		JSONObject result = null;

		try {
			
			final HttpResponse httpResponse = _http.get(SERVICE_URL + '/' + path).params(params).execute();
			final StringBuilder responseBuffer = new StringBuilder();
			httpResponse.read(responseBuffer);
			
			StringBuilder response = checkResponse(responseBuffer);			

			if ((response == null) || (response.length() <= 0)) {
				result = null;
			}else{
				result = new JSONObject(new JSONTokener(response.toString()));
			}
			
		} catch (HttpClientException e) {
			throw e;
		} catch (IOException e) {
			throw e;
		} catch (WebServiceException e) {
			throw e;
		} catch (Throwable t) {
			t.printStackTrace();
		}
		
		return result;
	}
	
	protected File readRaw(String path, boolean relative) throws HttpClientException, IOException {
		return readRaw(path, null, relative);
	}

	protected File readRaw(String path, Hashtable<String, String> params, boolean relative) throws HttpClientException, IOException {
		
		File result = null;

		try {
						
			path = (relative) ? SERVICE_URL + '/'+ path : path;
			
			result = new File(_context.getCacheDir(), new File(path).getName());
			_http.get(path).params(params).to(result).execute();
			
		}catch (HttpClientException e) {
			throw e;
		} catch (IOException e) {
			throw e;
		} catch (Throwable t) {
			t.printStackTrace();
		}
		
		return result;
	}
	
	protected JSONObject write(String path, Hashtable<String, String> params) throws HttpClientException, IOException, WebServiceException{

		JSONObject result = null;

		try {

			final HttpResponse httpResponse = _http.post(SERVICE_URL + '/' + path).params(params).execute();
			final StringBuilder responseBuffer = new StringBuilder();
			httpResponse.read(responseBuffer);
			
			StringBuilder response = checkResponse(responseBuffer);			

			if ((response == null) || (response.length() <= 0)) {
				result = null;
			}else{
				result = new JSONObject(new JSONTokener(response.toString()));
			}

		} catch (HttpClientException e) {
			throw e;
		} catch (IOException e) {
			throw e;
		} catch (WebServiceException e) {
			throw e;
		} catch (Throwable t) {
			t.printStackTrace();
		}
		
		return result;
	}
	
	/**
	 * Get current result page count
	 * 
	 * @return int - Current result page count
	 */
	public int getResultPageCount(){
		return _resultPageCount;
	}
	
	protected Celeb getCeleb(JSONObject jo){
		return new AmmCeleb(jo, getContext());
	}
	
	/**
	 * Get celebs
	 * 
	 * @param params - page parameters
	 *            
	 * @return Celeb[] - Array of Celeb object
	 * 
	 * @throws HttpClientException HttpClient Exception
	 * 
	 * @throws IOException Network/Connection error
	 * 
	 * @throws WebServiceException Error response from web service
	 */
	public Celeb[] getCelebs(Hashtable<String, String> params) throws HttpClientException, IOException, WebServiceException {

		Celeb[] result = null;

		try {
			
			JSONObject jo = read("celeb/list", params);
			
			if ((jo != null) && (jo.length() > 0)) {
				
				_resultPageCount = jo.optInt("pageCount");
				
				JSONArray ja = jo.optJSONArray("data");
				
				if ((ja != null) && (ja.length() > 0)) {
					
					result = new Celeb[ja.length()];
					
					for (int i = 0; i < ja.length(); i++) {
						result[i] = getCeleb(ja.optJSONObject(i));
					}
				}
			}
			
			
		} catch (HttpClientException e) {
			throw e;
		} catch (IOException e) {
			throw e;
		} catch (WebServiceException e) {
			throw e;
		} catch (Throwable t) {
			t.printStackTrace();
		}

		return result;
	}

	/**
	 * Asynchronously get celebs
	 * 
	 * @param params - Page parameters
	 * 
	 * @param listener - Subclass of AsyncCallback interface
	 *            
	 * @param state - Listener state
	 * 
	 * @return Celeb[] - Array of Celeb object           
	 */
	public void getCelebs(final Hashtable<String, String> params, final AsyncCallback listener, final Object state) {

		new Thread() {
			public void run() {

				try {
					Celeb[] result = getCelebs(params);
					try {
						listener.onComplete(result, state);
					} catch (Throwable t) {
						t.printStackTrace();
					}
				} catch (HttpClientException e) {
					listener.onHttpClientException(e, state);
				} catch (IOException e) {
					listener.onIOException(e, state);
				} catch (WebServiceException e) {
					listener.onWebServiceException(e, state);
				} catch (Throwable t) {
					t.printStackTrace();
				}
			}
		}.start();

	}

	/**
	 * Get celeb information
	 * 
	 * @param celebId - Celeb Id
	 * 
	 * @return Celeb - Celeb object
	 * 
	 * @throws HttpClientException HttpClient Exception
	 * 
	 * @throws IOException Network/Connection error
	 * 
	 * @throws WebServiceException Error response from web service
	 */
	public Celeb getCeleb(int celebId) throws HttpClientException, IOException, WebServiceException {

		Celeb result = null;
		
		Hashtable<String, String> params = new Hashtable<String, String>();
		params.put("celebId", Integer.toString(celebId));

		try {
			
			JSONObject jo = read("celeb/info", params);
			
			if ((jo != null) && (jo.length() > 0)) {
				result = getCeleb(jo);
			}
			
		} catch (HttpClientException e) {
			throw e;
		} catch (IOException e) {
			throw e;
		} catch (WebServiceException e) {
			throw e;
		} catch (Throwable t) {
			t.printStackTrace();
		}

		return result;
	}
	
	/**
	 * Asynchronously get celeb information
	 * 
	 * @param celebId - Celeb id
	 * 
	 * @param listener - Subclass of AsyncCallback interface
	 * 
	 * @param state - Listener state
	 *            
	 * @return Celeb - Celeb object           
	 */
	public void getCeleb(final int celebId, final AsyncCallback listener, final Object state) {

		new Thread() {

			public void run() {

				try {

					Celeb[] result = new Celeb[1];
					result[0] = getCeleb(celebId);

					try {
						listener.onComplete(result, state);
					} catch (Throwable t) {
						t.printStackTrace();
					}
				} catch (HttpClientException e) {
					listener.onHttpClientException(e, state);
				} catch (IOException e) {
					listener.onIOException(e, state);
				} catch (WebServiceException e) {
					listener.onWebServiceException(e, state);
				} catch (Throwable t) {
					t.printStackTrace();
				}
			}
		}.start();

	}
	
	protected Movie getMovie(JSONObject jo){
		return new AmmMovie(jo, getContext());
	}
	
	/**
	 * Get movies
	 * 
	 * @param params - page parameters
	 *            
	 * @return Movie[] - Array of Movie object
	 * 
	 * @throws HttpClientException HttpClient Exception
	 * 
	 * @throws IOException Network/Connection error
	 * 
	 * @throws WebServiceException Error response from web service
	 */
	public Movie[] getMovies(Hashtable<String, String> params) throws HttpClientException, IOException, WebServiceException {

		Movie[] result = null;

		try {
			
			JSONObject jo = read("movie/list", params);
			
			if ((jo != null) && (jo.length() > 0)) {
				
				_resultPageCount = jo.optInt("pageCount");
				
				JSONArray ja = jo.optJSONArray("data");
				
				if ((ja != null) && (ja.length() > 0)) {
					
					result = new Movie[ja.length()];
					
					for (int i = 0; i < ja.length(); i++) {
						result[i] = getMovie(ja.optJSONObject(i));
					}
				}
			}
			
		} catch (HttpClientException e) {
			throw e;
		} catch (IOException e) {
			throw e;
		} catch (WebServiceException e) {
			throw e;
		} catch (Throwable t) {
			t.printStackTrace();
		}

		return result;
	}
	
	/**
	 * Asynchronously get movies
	 * 
	 * @param params - Page parameters
	 * 
	 * @param listener - Subclass of AsyncCallback interface
	 *            
	 * @param state - Listener state
	 * 
	 * @return Movie[] - Array of Movie object           
	 */
	public void getMovies(final Hashtable<String, String> params, final AsyncCallback listener, final Object state) {

		new Thread() {
			public void run() {

				try {
					Movie[] result = getMovies(params);
					try {
						listener.onComplete(result, state);
					} catch (Throwable t) {
						t.printStackTrace();
					}
				} catch (HttpClientException e) {
					listener.onHttpClientException(e, state);
				} catch (IOException e) {
					listener.onIOException(e, state);
				} catch (WebServiceException e) {
					listener.onWebServiceException(e, state);
				} catch (Throwable t) {
					t.printStackTrace();
				}
			}
		}.start();

	}
	
	/**
	 * Get latest movies
	 *            
	 * @return Movie[] - Array of Movie object
	 * 
	 * @throws HttpClientException HttpClient Exception
	 * 
	 * @throws IOException Network/Connection error
	 * 
	 * @throws WebServiceException Error response from web service
	 */
	public Movie[] getLatestMovies() throws HttpClientException, IOException, WebServiceException {

		Movie[] result = null;

		try {
			
			JSONObject jo = read("movie/latest");
			
			if ((jo != null) && (jo.length() > 0)) {
				
				_resultPageCount = jo.optInt("pageCount");
				
				JSONArray ja = jo.optJSONArray("data");
				
				if ((ja != null) && (ja.length() > 0)) {
					
					result = new Movie[ja.length()];
					
					for (int i = 0; i < ja.length(); i++) {
						result[i] = getMovie(ja.optJSONObject(i));
					}
				}
			}
			
			
		} catch (HttpClientException e) {
			throw e;
		} catch (IOException e) {
			throw e;
		} catch (WebServiceException e) {
			throw e;
		} catch (Throwable t) {
			t.printStackTrace();
		}

		return result;
	}
	
	/**
	 * Asynchronously get latest movies
	 * 
	 * @param listener - Subclass of AsyncCallback interface
	 *            
	 * @param state - Listener state
	 * 
	 * @return Movie[] - Array of Movie object           
	 */
	public void getLatestMovies(final AsyncCallback listener, final Object state) {

		new Thread() {
			public void run() {

				try {
					Movie[] result = getLatestMovies();
					try {
						listener.onComplete(result, state);
					} catch (Throwable t) {
						t.printStackTrace();
					}
				}catch (HttpClientException e) {
					listener.onHttpClientException(e, state);
				} catch (IOException e) {
					listener.onIOException(e, state);
				} catch (WebServiceException e) {
					listener.onWebServiceException(e, state);
				} catch (Throwable t) {
					t.printStackTrace();
				}
			}
		}.start();
	}
	
	/**
	 * Get week movies
	 *            
	 * @return Movie[] - Array of Movie object
	 * 
	 * @throws HttpClientException HttpClient Exception
	 * 
	 * @throws IOException Network/Connection error
	 * 
	 * @throws WebServiceException Error response from web service
	 */
	public Movie[] getWeekMovies() throws HttpClientException, IOException, WebServiceException {

		Movie[] result = null;

		try {
			
			JSONObject jo = read("movie/listweek");
			
			if ((jo != null) && (jo.length() > 0)) {
				
				_resultPageCount = jo.optInt("pageCount");
				
				JSONArray ja = jo.optJSONArray("data");
				
				if ((ja != null) && (ja.length() > 0)) {
					
					result = new Movie[ja.length()];
					
					for (int i = 0; i < ja.length(); i++) {
						result[i] = getMovie(ja.optJSONObject(i));
					}
				}
			}
			
			
		} catch (HttpClientException e) {
			throw e;
		} catch (IOException e) {
			throw e;
		} catch (WebServiceException e) {
			throw e;
		} catch (Throwable t) {
			t.printStackTrace();
		}

		return result;
	}
	
	/**
	 * Asynchronously get week movies
	 * 
	 * @param listener - Subclass of AsyncCallback interface
	 *            
	 * @param state - Listener state
	 * 
	 * @return Movie[] - Array of Movie object           
	 */
	public void getWeekMovies(final AsyncCallback listener, final Object state) {

		new Thread() {
			public void run() {

				try {
					Movie[] result = getWeekMovies();
					try {
						listener.onComplete(result, state);
					} catch (Throwable t) {
						t.printStackTrace();
					}
				}catch (HttpClientException e) {
					listener.onHttpClientException(e, state);
				} catch (IOException e) {
					listener.onIOException(e, state);
				} catch (WebServiceException e) {
					listener.onWebServiceException(e, state);
				} catch (Throwable t) {
					t.printStackTrace();
				}
			}
		}.start();
	}
	
	/**
	 * Get top rated movies
	 *            
	 * @return Movie[] - Array of Movie object
	 * 
	 * @throws HttpClientException HttpClient Exception
	 * 
	 * @throws IOException Network/Connection error
	 * 
	 * @throws WebServiceException Error response from web service
	 */
	public Movie[] getTopRatedMovies() throws HttpClientException, IOException, WebServiceException {

		Movie[] result = null;

		try {
			
			JSONObject jo = read("movie/toprated");
			
			if ((jo != null) && (jo.length() > 0)) {
				
				_resultPageCount = jo.optInt("pageCount");
				
				JSONArray ja = jo.optJSONArray("data");
				
				if ((ja != null) && (ja.length() > 0)) {
					
					result = new Movie[ja.length()];
					
					for (int i = 0; i < ja.length(); i++) {
						result[i] = getMovie(ja.optJSONObject(i));
					}
				}
			}
			
			
		} catch (HttpClientException e) {
			throw e;
		} catch (IOException e) {
			throw e;
		} catch (WebServiceException e) {
			throw e;
		} catch (Throwable t) {
			t.printStackTrace();
		}

		return result;
	}
	
	/**
	 * Asynchronously get top rated movies
	 * 
	 * @param listener - Subclass of AsyncCallback interface
	 *            
	 * @param state - Listener state
	 * 
	 * @return Movie[] - Array of Movie object           
	 */
	public void getTopRatedMovies(final AsyncCallback listener, final Object state) {

		new Thread() {
			public void run() {

				try {
					Movie[] result = getTopRatedMovies();
					try {
						listener.onComplete(result, state);
					} catch (Throwable t) {
						t.printStackTrace();
					}
				}catch (HttpClientException e) {
					listener.onHttpClientException(e, state);
				} catch (IOException e) {
					listener.onIOException(e, state);
				} catch (WebServiceException e) {
					listener.onWebServiceException(e, state);
				} catch (Throwable t) {
					t.printStackTrace();
				}
			}
		}.start();
	}
	
	/**
	 * Get movie information
	 * 
	 * @param movieId - Movie Id
	 * 
	 * @return Movie - Movie object
	 * 
	 * @throws HttpClientException HttpClient Exception
	 * 
	 * @throws IOException Network/Connection error
	 * 
	 * @throws WebServiceException Error response from web service
	 */
	public Movie getMovie(int movieId) throws HttpClientException, IOException, WebServiceException {

		Movie result = null;
		
		Hashtable<String, String> params = new Hashtable<String, String>();
		params.put("movieId", Integer.toString(movieId));
		params.put("deviceType", DEVICE_TYPE);

		try {
			
			JSONObject jo = read("movie/info", params);
			
			if ((jo != null) && (jo.length() > 0)) {
				result = getMovie(jo);
			}
			
		} catch (HttpClientException e) {
			throw e;
		} catch (IOException e) {
			throw e;
		} catch (WebServiceException e) {
			throw e;
		} catch (Throwable t) {
			t.printStackTrace();
		}

		return result;
	}
	
	/**
	 * Asynchronously get movie information
	 * 
	 * @param movieId - Movie id
	 * 
	 * @param listener - Subclass of AsyncCallback interface
	 * 
	 * @param state - Listener state
	 *            
	 * @return Movie - Movie object
	 */
	public void getMovie(final int movieId, final AsyncCallback listener, final Object state) {

		new Thread() {

			public void run() {

				try {

					Movie[] result = new Movie[1];
					result[0] = getMovie(movieId);

					try {
						listener.onComplete(result, state);
					} catch (Throwable t) {
						t.printStackTrace();
					}
				} catch (HttpClientException e) {
					listener.onHttpClientException(e, state);
				} catch (IOException e) {
					listener.onIOException(e, state);
				} catch (WebServiceException e) {
					listener.onWebServiceException(e, state);
				} catch (Throwable t) {
					t.printStackTrace();
				}
			}
		}.start();
	}
	
	protected User getUser(JSONObject jo){
		return new AmmUser(jo, getContext());
	}
	
	/**
	 * Get user information
	 * 
	 * @param email - User email address
	 * 
	 * @return User - User object
	 * 
	 * @throws HttpClientException HttpClient Exception
	 * 
	 * @throws IOException Network/Connection error
	 * 
	 * @throws WebServiceException Error response from web service
	 */
	public User getUser(String email) throws HttpClientException, IOException, WebServiceException {

		User result = null;
		
		Hashtable<String, String> params = new Hashtable<String, String>();
		params.put("email", email);

		try {
			
			JSONObject jo = read("user/info", params);
			
			if ((jo != null) && (jo.length() > 0)) {
				result = getUser(jo);
			}
			
		}catch (HttpClientException e) {
			throw e;
		} catch (IOException e) {
			throw e;
		} catch (WebServiceException e) {
			throw e;
		} catch (Throwable t) {
			t.printStackTrace();
		}

		return result;
	}
	
	/**
	 * Asynchronously get user information
	 * 
	 * @param email - User email address
	 * 
	 * @param listener - Subclass of AsyncCallback interface
	 * 
	 * @param state - Listener state
	 *            
	 * @return User - User object           
	 */
	public void getUser(final String email, final AsyncCallback listener, final Object state) {

		new Thread() {

			public void run() {

				try {

					User[] result = new User[1];
					result[0] = getUser(email);

					try {
						listener.onComplete(result, state);
					} catch (Throwable t) {
						t.printStackTrace();
					}
				} catch (HttpClientException e) {
					listener.onHttpClientException(e, state);
				} catch (IOException e) {
					listener.onIOException(e, state);
				} catch (WebServiceException e) {
					listener.onWebServiceException(e, state);
				} catch (Throwable t) {
					t.printStackTrace();
				}
			}
		}.start();
	}
	
	/**
	 * Authenticate user login credentials
	 * 
	 * @param email - User email
	 * 
	 * @param password - User password
	 * 
	 * @return boolean - True if user login credentials is correct; otherwise, false.
	 * 
	 * @throws HttpClientException HttpClient Exception
	 * 
	 * @throws IOException Network/Connection error
	 * 
	 * @throws WebServiceException Error response from web service
	 */
	public boolean authenticateUser(String email, String password) throws HttpClientException, IOException, WebServiceException {
		
		boolean result = false;
		
		Hashtable<String, String> data = new Hashtable<String, String>();
		data.put("email", email);
		data.put("password", password);

		try {

			JSONObject jo = write("user/login", data);
			
			if ((jo != null) && (jo.length() > 0)) {
				if((jo.has("message")) && (jo.optString("message").equalsIgnoreCase("success"))){
					result = true;
				}	
			}

		} catch (HttpClientException e) {
			throw e;
		} catch (IOException e) {
			throw e;
		} catch (WebServiceException e) {
			throw e;
		} catch (Throwable t) {
			t.printStackTrace();
		}

		return result;
	}
	
	/**
	 * Asynchronously authenticate user login credentials
	 * 
	 * @param email - User email
	 * 
	 * @param password - User password
	 * 
	 * @param listener - Subclass of AsyncCallback interface
	 * 
	 * @param state - Listener state
	 * 
	 * @return boolean - True if user login credentials is correct; otherwise, false.
	 */
	public void authenticateUser(final String email, final String password, final AsyncCallback listener, final Object state) {

		new Thread() {
			public void run() {

				try {
					boolean[] result = new boolean[1]; 
					
					result[0] = authenticateUser(email, password);
					
					try {
						listener.onComplete(result, state);
					} catch (Throwable t) {
						t.printStackTrace();
					}
				} catch (HttpClientException e) {
					listener.onHttpClientException(e, state);
				} catch (IOException e) {
					listener.onIOException(e, state);
				} catch (WebServiceException e) {
					listener.onWebServiceException(e, state);
				} catch (Throwable t) {
					t.printStackTrace();
				}
			}
		}.start();
	}
	
	/**
	 * Add user information
	 * 
	 * @param data - User information
	 * 
	 * @return boolean - True if the insert was successful; otherwise, false.
	 * 
	 * @throws HttpClientException HttpClient Exception
	 * 
	 * @throws IOException Network/Connection error
	 * 
	 * @throws WebServiceException Error response from web service
	 */
	public boolean addUser(Hashtable<String, String> data) throws HttpClientException, IOException, WebServiceException {
		
		boolean result = false;

		try {

			JSONObject jo = write("user/add", data);
			
			if ((jo != null) && (jo.length() > 0)) {
				if((jo.has("message")) && (jo.optString("message").equalsIgnoreCase("success"))){
					result = true;
				}	
			}

		} catch (HttpClientException e) {
			throw e;
		} catch (IOException e) {
			throw e;
		} catch (WebServiceException e) {
			throw e;
		} catch (Throwable t) {
			t.printStackTrace();
		}

		return result;
	}
	
	/**
	 * Asynchronously add user information
	 * 
	 * @param data - User information
	 * 
	 * @param listener - Subclass of AsyncCallback interface
	 * 
	 * @param state - Listener state
	 * 
	 * @return boolean - True if the insert was successful; otherwise, false.
	 */
	public void addUser(final Hashtable<String, String> data, final AsyncCallback listener, final Object state) {

		new Thread() {
			public void run() {

				try {
					boolean[] result = new boolean[1]; 
					result[0] = addUser(data);
					try {
						listener.onComplete(result, state);
					} catch (Throwable t) {
						t.printStackTrace();
					}
				} catch (HttpClientException e) {
					listener.onHttpClientException(e, state);
				} catch (IOException e) {
					listener.onIOException(e, state);
				} catch (WebServiceException e) {
					listener.onWebServiceException(e, state);
				} catch (Throwable t) {
					t.printStackTrace();
				}
			}
		}.start();
	}
	
	/**
	 * Reset user password
	 * 
	 * @param email - User email address
	 * 
	 * @return boolean - True if the password reset was successful; otherwise, false.
	 * 
	 * @throws HttpClientException HttpClient Exception
	 * 
	 * @throws IOException Network/Connection error
	 * 
	 * @throws WebServiceException Error response from web service
	 */
	public boolean resetUserPassword(String email) throws HttpClientException, IOException, WebServiceException {
		
		boolean result = false;
		
		Hashtable<String, String> data = new Hashtable<String, String>();
		data.put("email", email);

		try {

			JSONObject jo = write("user/resetpassword", data);
			
			if ((jo != null) && (jo.length() > 0)) {
				if((jo.has("message")) && (jo.optString("message").equalsIgnoreCase("success"))){
					result = true;
				}	
			}

		} catch (HttpClientException e) {
			throw e;
		} catch (IOException e) {
			throw e;
		} catch (WebServiceException e) {
			throw e;
		} catch (Throwable t) {
			t.printStackTrace();
		}

		return result;
	}
	
	/**
	 * Asynchronously reset user password
	 * 
	 * @param email - User email address
	 * 
	 * @param listener - Subclass of AsyncCallback interface
	 * 
	 * @param state - Listener state
	 * 
	 * @return boolean - True if the password reset was successful; otherwise, false.
	 */
	public void resetUserPassword(final String email, final AsyncCallback listener, final Object state) {

		new Thread() {
			public void run() {

				try {
					boolean[] result = new boolean[1]; 
					result[0] = resetUserPassword(email);
					try {
						listener.onComplete(result, state);
					} catch (Throwable t) {
						t.printStackTrace();
					}
				} catch (HttpClientException e) {
					listener.onHttpClientException(e, state);
				} catch (IOException e) {
					listener.onIOException(e, state);
				} catch (WebServiceException e) {
					listener.onWebServiceException(e, state);
				} catch (Throwable t) {
					t.printStackTrace();
				}
			}
		}.start();
	}
	
	protected Country getCountry(JSONObject jo){
		return new AmmCountry(jo, getContext());
	}
	
	/**
	 * Get countries
	 *            
	 * @return Country[] - Array of Country object
	 * 
	 * @throws HttpClientException HttpClient Exception
	 * 
	 * @throws IOException Network/Connection error
	 * 
	 * @throws WebServiceException Error response from web service
	 */
	public Country[] getCountries() throws HttpClientException, IOException, WebServiceException {

		Country[] result = null;

		try {
			
			JSONObject jo = read("misc/country");
			
			if ((jo != null) && (jo.length() > 0)) {
								
				JSONArray ja = jo.optJSONArray("data");
				
				if ((ja != null) && (ja.length() > 0)) {
					
					result = new Country[ja.length()];
					
					for (int i = 0; i < ja.length(); i++) {
						result[i] = getCountry(ja.optJSONObject(i));
					}
				}
			}
			
			
		} catch (HttpClientException e) {
			throw e;
		} catch (IOException e) {
			throw e;
		} catch (WebServiceException e) {
			throw e;
		} catch (Throwable t) {
			t.printStackTrace();
		}

		return result;
	}
	
	/**
	 * Asynchronously get countries
	 * 
	 * @param listener - Subclass of AsyncCallback interface
	 *            
	 * @param state - Listener state
	 * 
	 * @return Country[] - Array of Country object           
	 */
	public void getCountries(final AsyncCallback listener, final Object state) {

		new Thread() {
			public void run() {

				try {
					Country[] result = getCountries();
					try {
						listener.onComplete(result, state);
					} catch (Throwable t) {
						t.printStackTrace();
					}
				} catch (HttpClientException e) {
					listener.onHttpClientException(e, state);
				} catch (IOException e) {
					listener.onIOException(e, state);
				} catch (WebServiceException e) {
					listener.onWebServiceException(e, state);
				} catch (Throwable t) {
					t.printStackTrace();
				}
			}
		}.start();
	}
	
	/**
	 * Report Problem
	 * 
	 * @param data - Error information
	 * 
	 * @return boolean - True if the report have been successfully sent; otherwise, false.
	 * 
	 * @throws HttpClientException HttpClient Exception
	 * 
	 * @throws IOException Network/Connection error
	 * 
	 * @throws WebServiceException Error response from web service
	 */
	public boolean reportProblem(Hashtable<String, String> data) throws HttpClientException, IOException, WebServiceException {
		
		boolean result = false;

		try {

			JSONObject jo = write("misc/reportproblem", data);
			
			if ((jo != null) && (jo.length() > 0)) {
				if((jo.has("message")) && (jo.optString("message").equalsIgnoreCase("success"))){
					result = true;
				}	
			}

		} catch (HttpClientException e) {
			throw e;
		} catch (IOException e) {
			throw e;
		} catch (WebServiceException e) {
			throw e;
		} catch (Throwable t) {
			t.printStackTrace();
		}

		return result;
	}
	
	/**
	 * Asynchronously Report Problem
	 * 
	 * @param data - Error information
	 * 
	 * @param listener - Subclass of AsyncCallback interface
	 * 
	 * @param state - Listener state
	 * 
	 * @return boolean - True if the report have been successfully sent; otherwise, false.
	 */
	public void reportProblem(final Hashtable<String, String> data, final AsyncCallback listener, final Object state) {

		new Thread() {
			public void run() {

				try {
					boolean[] result = new boolean[1]; 
					result[0] = reportProblem(data);
					try {
						listener.onComplete(result, state);
					} catch (Throwable t) {
						t.printStackTrace();
					}
				} catch (HttpClientException e) {
					listener.onHttpClientException(e, state);
				} catch (IOException e) {
					listener.onIOException(e, state);
				} catch (WebServiceException e) {
					listener.onWebServiceException(e, state);
				} catch (Throwable t) {
					t.printStackTrace();
				}
			}
		}.start();
	}
	
	/**
	 * Redeem Token
	 * 
	 * @param email - User email address
	 * 
	 * @param token - The token number to redeem
	 * 
	 * @return boolean - True if the token have been successfully redeemed; otherwise, false.
	 * 
	 * @throws HttpClientException HttpClient Exception
	 * 
	 * @throws IOException Network/Connection error
	 * 
	 * @throws WebServiceException Error response from web service
	 */
	public boolean redeemToken(String email, String token) throws HttpClientException, IOException, WebServiceException {
		
		boolean result = false;
		
		Hashtable<String, String> data = new Hashtable<String, String>();
		data.put("email", email);
		data.put("token", token);

		try {

			JSONObject jo = write("token/redeem", data);
			
			if ((jo != null) && (jo.length() > 0)) {
				if((jo.has("message")) && (jo.optString("message").equalsIgnoreCase("success"))){
					result = true;
				}	
			}

		} catch (HttpClientException e) {
			throw e;
		} catch (IOException e) {
			throw e;
		} catch (WebServiceException e) {
			throw e;
		} catch (Throwable t) {
			t.printStackTrace();
		}

		return result;
	}
	
	/**
	 * Asynchronously Redeem Token
	 * 
	 * @param email - User email address
	 * 
	 * @param token - The token number to redeem
	 * 
	 * @param listener - Subclass of AsyncCallback interface
	 * 
	 * @param state - Listener state
	 * 
	 * @return boolean - True if the token have been successfully redeemed; otherwise, false.
	 */
	public void redeemToken(final String email, final String token, final AsyncCallback listener, final Object state) {

		new Thread() {
			public void run() {

				try {
					boolean[] result = new boolean[1]; 
					result[0] = redeemToken(email, token);
					try {
						listener.onComplete(result, state);
					} catch (Throwable t) {
						t.printStackTrace();
					}
				} catch (HttpClientException e) {
					listener.onHttpClientException(e, state);
				} catch (IOException e) {
					listener.onIOException(e, state);
				} catch (WebServiceException e) {
					listener.onWebServiceException(e, state);
				} catch (Throwable t) {
					t.printStackTrace();
				}
			}
		}.start();
	}

	/**
	 * Get user token balance
	 * 
	 * @param email - User email address
	 * 
	 * @return int - User token balance
	 * 
	 * @throws HttpClientException HttpClient Exception
	 * 
	 * @throws IOException Network/Connection error
	 * 
	 * @throws WebServiceException Error response from web service
	 */
	public int getUserTokenBalance(String email) throws HttpClientException, IOException, WebServiceException {
		
		int result = 0;
		
		Hashtable<String, String> params = new Hashtable<String, String>();
		params.put("email", email);

		try {

			JSONObject jo = read("token/balance", params);
			
			if ((jo != null) && (jo.length() > 0)) {
				if((jo.has("balance"))){
					result = jo.optInt("balance");
				}	
			}

		} catch (HttpClientException e) {
			throw e;
		} catch (IOException e) {
			throw e;
		} catch (WebServiceException e) {
			throw e;
		} catch (Throwable t) {
			t.printStackTrace();
		}

		return result;
	}
	
	/**
	 * Asynchronously get user token balance
	 * 
	 * @param email - User email address
	 * 
	 * @param listener - Subclass of AsyncCallback interface
	 * 
	 * @param state - Listener state
	 * 
	 * @return int - User token balance
	 */
	public void getUserTokenBalance(final String email, final AsyncCallback listener, final Object state) {

		new Thread() {
			public void run() {

				try {
					int[] result = new int[1]; 
					result[0] = getUserTokenBalance(email);
					try {
						listener.onComplete(result, state);
					} catch (Throwable t) {
						t.printStackTrace();
					}
				} catch (HttpClientException e) {
					listener.onHttpClientException(e, state);
				} catch (IOException e) {
					listener.onIOException(e, state);
				} catch (WebServiceException e) {
					listener.onWebServiceException(e, state);
				} catch (Throwable t) {
					t.printStackTrace();
				}
			}
		}.start();
	}

	/**
	 * Search African Movies Mobile
	 * 
	 * @param params - search parameters (query, email)
	 *            
	 * @return Object[] - Array of Object
	 * 
	 * @throws HttpClientException HttpClient Exception
	 * 
	 * @throws IOException Network/Connection error
	 * 
	 * @throws WebServiceException Error response from web service
	 */
	public Object[] searchAmm(Hashtable<String, String> params) throws HttpClientException, IOException, WebServiceException {

		Object[] result = new Object[2];

		try {
			
			JSONObject jo = read("misc/search", params);
			
			if ((jo != null) && (jo.length() > 0)) {
								
				JSONArray movies = jo.optJSONArray("movies");
				JSONArray celebs = jo.optJSONArray("celebs");
				
				if ((movies != null) && (movies.length() > 0)) {
					
					Movie[] resultMovies = null;
					
					resultMovies = new Movie[movies.length()];
					
					for (int i = 0; i < movies.length(); i++) {
						resultMovies[i] = getMovie(movies.optJSONObject(i));
					}
					
					result[0] = resultMovies;
				}
				
				if ((celebs != null) && (celebs.length() > 0)) {
					
					Celeb[] resultCelebs = null;
					
					resultCelebs = new Celeb[celebs.length()];
					
					for (int i = 0; i < celebs.length(); i++) {
						resultCelebs[i] = getCeleb(celebs.optJSONObject(i));
					}
					
					result[1] = resultCelebs;
				}
			}
			
		} catch (HttpClientException e) {
			throw e;
		} catch (IOException e) {
			throw e;
		} catch (WebServiceException e) {
			throw e;
		} catch (Throwable t) {
			t.printStackTrace();
		}

		return result;
	}
	
	/**
	 * Asynchronously search African Movies Mobile
	 * 
	 * @param params - search parameters (query, email)
	 * 
	 * @param listener - Subclass of AsyncCallback interface
	 * 
	 * @param state - Listener state
	 * 
	 * @return Object[] - Array of Object
	 */
	public void searchAmm(final Hashtable<String, String> params, final AsyncCallback listener, final Object state) {

		new Thread() {
			public void run() {

				try {
					
					Object[] result = searchAmm(params);
					
					try {
						listener.onComplete(result, state);
					} catch (Throwable t) {
						t.printStackTrace();
					}
				} catch (HttpClientException e) {
					listener.onHttpClientException(e, state);
				} catch (IOException e) {
					listener.onIOException(e, state);
				} catch (WebServiceException e) {
					listener.onWebServiceException(e, state);
				} catch (Throwable t) {
					t.printStackTrace();
				}
			}
		}.start();
	}
	
	public boolean isStub() {
		return false;
	}

}
